﻿using System;
using System.Collections.Generic;
using System.Text;
using Foothill.Model;
using Foothill.Service.AdService;
using Foothill.Service.SystemMessage;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Foothill.Service.Test
{
    [TestClass]
    public class PostValidatorTest
    {
        private PostValidator _validator;

        [TestInitialize]
        public void Setup()
        {
            var mock = new Mock<ISystemMessageService>();
            _validator = new PostValidator(mock.Object);
        }

        [TestMethod]
        public void IsNonNegativeNumber_ShouldReturnFalse_WhenInputIsNull()
        {
            int convertedNumber;
            bool result = _validator.IsNonNegativeNumber(string.Empty, out convertedNumber);
            Assert.IsFalse(result);
        }

        [TestMethod]
        public void IsNonNegativeNumber_ShouldReturnFalse_WhenInputIsNotNumber()
        {
            int convertedNumber;
            bool result = _validator.IsNonNegativeNumber("a", out convertedNumber);
            Assert.IsFalse(result);
        }

        [TestMethod]
        public void IsNonNegativeNumber_ShouldReturnTrue_WhenInputIsDecimal()
        {
            int convertedNumber;
            bool result = _validator.IsNonNegativeNumber("123.4", out convertedNumber);
            Assert.IsTrue(result);
        }

        [TestMethod]
        public void IsNonNegativeNumber_ShouldConvertToInt_WhenInputIsDecimal()
        {
            int convertedNumber;
            bool result = _validator.IsNonNegativeNumber("123.9", out convertedNumber);
            Assert.AreEqual(123, convertedNumber);
        }

        [TestMethod]
        public void IsNonNegativeNumber_ShouldReturnFalse_WhenInputIsNegative()
        {
            int convertedNumber;
            bool result = _validator.IsNonNegativeNumber("-99.9", out convertedNumber);
            Assert.IsFalse(result);
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenMakeIdIsNonPositive()
        {
            ForSaleAd newPost = ConstructNewSaleAd(0, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "mail@google.com");
            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.ModelId));
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenPriceStrIsNonPositive()
        {
            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "mail@google.com");
            newPost.PriceStr = "-1";
            
            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.Price));
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenPriceStrIsNotNumber()
        {
            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "mail@google.com");
            newPost.PriceStr = "a";

            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.Price));
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenPriceStrIsNull()
        {
            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "mail@google.com");
            newPost.PriceStr = null;

            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.Price));
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenMileageStrIsNotNumber()
        {
            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "mail@google.com");
            newPost.MileAgeStr = "a";

            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.MileAge));
        }

        [TestMethod]
        public void ShouldReturnTrue_WhenMileageStrIsPositiveLong()
        {
            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "mail@google.com");
            newPost.MileAgeStr = "11.2";

            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsTrue(isValid);
            Assert.IsTrue(invalidFields.Keys.Count == 0);
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenInvalidEmailIsUsed()
        {
            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "invalidEmailAddress");

            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.ContactEmail));
        }

        [TestMethod]
        public void ShouldReturnFalse_WhenDescriptionExceedsLimit()
        {
            var mock = new Mock<ForSaleAd>();

            StringBuilder sb = new StringBuilder();
            int limit = Foothill.Common.GlobalSettings.AdDescriptionMaxNumOfChars + 2;

            for (int i = 0; i < limit; ++i)
            {
                sb.Append("a");
            }

            ForSaleAd newPost = ConstructNewSaleAd(1, 0, VehicleCondition.New, 2001, 1000, TransmissionType.Automatic, 3000, ListingTypes.Dealer, 1, "778 888 9999", "invalidEmail@Address.com");
            newPost.Description = sb.ToString();

            IDictionary<PostForSaleFields, string> invalidFields = new Dictionary<PostForSaleFields, string>();
            bool isValid = _validator.ValidateForSaleAd(newPost, out invalidFields);

            Assert.IsFalse(isValid);
            Assert.IsTrue(invalidFields.Keys.Contains(PostForSaleFields.Description));
        }

        internal static ForSaleAd ConstructNewSaleAd(int modelId, int makeId, VehicleCondition condition,
           int year, int mileAge, TransmissionType transmissionType, int price, ListingTypes listingType,
           int cityId, string phoneNumber, string email)
        {
            ForSaleAd newAd = new ForSaleAd();

            newAd.ModelId = modelId;
            newAd.MakeId = makeId;
            newAd.VehicleConditionId = Convert.ToInt32(condition);
            newAd.YearMade = year;
            newAd.MileAge = mileAge;
            newAd.MileAgeStr = mileAge.ToString();
            newAd.TransmissionTypeId = Convert.ToInt32(transmissionType);
            newAd.Price = price;
            newAd.PriceStr = price.ToString();
            newAd.ListingTypeId = Convert.ToInt32(listingType);
            newAd.CityId = cityId;
            newAd.ContactPhone = phoneNumber;
            newAd.ContactEmail = email;
            newAd.TimePosted = DateTime.Now;
            newAd.LastEditTime = DateTime.Now;
            newAd.StatusId = (int)AdStatusLookup.Active;

            return newAd;
        }
    }
}
